use super::ContainerError;
use super::ContainerId;
use super::ContainerResult;
use super::HalfContainerInfo;
use super::container_position;
use super::item::{ContainerWriteItem,ContainerReadItem};
use super::read_err_from;
use super::seek_err_from;

use crate::constants::BACKUP_CONTAINER_LOG;
use crate::constants::CONTAINER_SIZE;
use crate::constants::RESTORE_CONTAINER_LOG;
use crate::container::write_err_from;

use std::io::{Read,Write,Seek,IoSlice,SeekFrom};
use std::sync::Arc;
use std::fs::File;
use util::{LruCacheItem,UnsafeBuf,Cp,MakeCp,RawData};


pub type ContainerHeader = u32;
pub const CAP_INIT_SIZE:usize = CONTAINER_SIZE - std::mem::size_of::<ContainerHeader>();

pub struct ContainerWriter<T:ContainerWriteItem> {
	cap:usize,
	id:ContainerId,
	items:Vec<Arc<T>>,
	hidden:Option<HalfContainerInfo>,
}

impl<T:ContainerWriteItem> ContainerWriter<T> {
	pub fn new(id:ContainerId) -> Self {
		Self {
			id:id,
			cap:CAP_INIT_SIZE,
			items:Vec::new(),
			hidden:None,
		}
	}

	pub fn half(id:ContainerId,info:HalfContainerInfo) -> Self {
		Self {
			id:id,
			cap:info.cap(),
			items:Vec::new(),
			hidden:Some(info),
		}
	}
	
	pub fn id(&self) -> ContainerId {
		self.id
	}
	
	pub fn cap(&self) -> usize {
		self.cap
	}
	
	pub fn used(&self) -> usize {
		CONTAINER_SIZE - self.cap
	}

	pub fn items(&self) -> &Vec<Arc<T>> {
		&self.items
	}

	pub fn hidden_item_num(&self) -> u32 {
		if let Some(info) = &self.hidden {
			info.header()
		} else {
			0
		}
	}

	pub fn all_item_num(&self) -> u32 {
		self.items.len() as u32 + self.hidden_item_num()
	}

	pub fn half_info(&self) -> HalfContainerInfo {
		HalfContainerInfo::new(self.all_item_num(),self.cap)
	}
	
	pub fn append_item(&mut self,item:Arc<T>) -> Result<usize,usize> {
		let item_len = item.len();
		if self.cap >= item_len {
			let no = self.all_item_num() as usize;
			log::debug!(target:BACKUP_CONTAINER_LOG,"container {} add no {} 0x{:0x} size chunk at 0x{:0x}\n"
												,self.id(),no,item.data_len(),self.used());
			self.cap -= item_len;
			self.items.push(item);
			Ok(no)
		} else {
			Err(item_len)
		}
		
	}

	fn half_on_write(&self,file:&mut File,skip:usize) -> ContainerResult<()> {
		if self.items.len() == 0 { return Ok(()); }
		let items_len:ContainerHeader = self.all_item_num();
		file.write(items_len.as_raw_data()).map_err(write_err_from)?;
		file.seek(SeekFrom::Current(skip.try_into().unwrap())).map_err(write_err_from)?;
		log::debug!(target:BACKUP_CONTAINER_LOG,"container {} half write at 0x{:0x} in file\n",self.id(),file.stream_position().unwrap());
		let mut io_slices = Vec::new();
		for t in &self.items {
			t.write_slice_to(&mut io_slices);
		}
		match file.write_all_vectored(&mut io_slices) {
			Ok(()) => {
				log::debug!(target:BACKUP_CONTAINER_LOG,"container {} has been writen with 0x{:0x} bytes\n",self.id(),self.used());
				Ok(())
			}
			Err(e) => {
				Err(write_err_from(e))
			}
		}
	}
	
	pub fn on_write(&self,file:&mut File) -> ContainerResult<()> {
		if let Some(info) = &self.hidden {
			let skip = CAP_INIT_SIZE - info.cap();
			return self.half_on_write(file, skip);
		}
		let items_len:ContainerHeader = self.items.len().try_into().unwrap();
		let mut io_slices = Vec::new();
		io_slices.push(IoSlice::new(items_len.as_raw_data()));
		for t in &self.items {
			t.write_slice_to(&mut io_slices);
		}
		match file.write_all_vectored(&mut io_slices) {
			Ok(()) => {
				log::debug!(target:BACKUP_CONTAINER_LOG,"container {} has been writen with 0x{:0x} bytes\n",self.id(),self.used());
				Ok(())
			}
			Err(e) => {
				Err(write_err_from(e))
			}
		}
	}

	pub fn get(&self,no:u32) -> Arc<T> {
		if no < self.items.len().try_into().unwrap() {
			self.items[no as usize].clone()
		} else {
			panic!("container {} get no {} error",self.id(),no);
		}
	}	
}

pub struct ContainerReader<T:ContainerReadItem> {
	id:ContainerId,
	items:Vec<Arc<T>>,
}

impl<T:ContainerReadItem> ContainerReader<T> {
	pub fn new(id:ContainerId) -> Self {
		Self {
			id:id,
			items:Vec::new(),
		}
	}
	
	pub fn id(&self) -> ContainerId {
		self.id
	}

	pub fn items(&self) -> &Vec<Arc<T>> {
		&self.items
	}

	pub fn on_read<F:Seek+Read>(&mut self,file:&mut F) -> ContainerResult<usize> {
		let buf = Arc::new(UnsafeBuf::new(CONTAINER_SIZE));
		let byte_num = unsafe { 
			match file.read(buf.get()) {
				Ok(num) => {
					num
				}
				Err(e) => {
					return Err(read_err_from(e));
				}
			}
		};
		log::debug!(target:RESTORE_CONTAINER_LOG,"container {} has been read with 0x{:0x} bytes\n",self.id(),byte_num);
		if byte_num == 0 {
			return Err(ContainerError::ReadNone(self.id(), 0));
		}
		let chunk_num:Cp<ContainerHeader> = buf.make_const_pointer().unwrap();
		for chunk_no in 0..*chunk_num {
			let read_position = buf.cursor();
			match T::read_slice_from(&buf) {
				Some(t) => {
					log::debug!(target:RESTORE_CONTAINER_LOG,"container {} has been read with no {} 0x{:0x} size chunk at 0x{:0x}\n"
															,self.id(),chunk_no,t.len(),read_position);
					self.items.push(t);
				}
				None => {
					return Err(ContainerError::ReadNone(self.id(), read_position));
				}
			}
		}
		Ok(byte_num)
	}
	
	pub fn get(&self,no:u32) -> Arc<T> {
		if no < self.items.len().try_into().unwrap() {
			self.items[no as usize].clone()
		} else {
			panic!("container {} get no {} error",self.id(),no);
		}
	}	
	
	pub fn get_all(&self) -> &[Arc<T>] {
		&self.items
	}
}

impl<T:ContainerReadItem> LruCacheItem for ContainerReader<T> {
	type ID = ContainerId;
	type SRC = File;
	type Error = ContainerError;
	fn id(&self) -> Self::ID {
		self.id
	}
	
	fn miss_read(src:&mut Self::SRC,id:Self::ID) -> Result<Arc<Self>,Self::Error> {
		let mut con:Arc<ContainerReader<T>> = Arc::new(ContainerReader::new(id));
		src.seek(SeekFrom::Start(container_position(id))).map_err(seek_err_from)?;
		Arc::get_mut(&mut con).unwrap().on_read(src)?;
		Ok(con)
	}
}
